// function identity(arg: number): number {
//     return arg;
// }

// function identity(arg: any): any {
//     return arg;
// }

function identity<Type>(arg: Type): Type {
    return arg;
}

// let output = identity<string>('');

// type argument inference
// let output = identity('');

// function loggingIdentity<Type>(arg: Type): Type {
//     // 类型“Type”上不存在属性“length”。
//     // console.log(arg.length);
//     return arg;
// }

function loggingIdentity<Type>(arg: Type[]): Type[] {
    console.log(arg.length);
    return arg;
}

// ----------------
// Generics Types
// ----------------

let foo: <Type>(arg: Type[]) => Type[] = loggingIdentity;

let bar: { <Type>(arg: Type[]): Type[] } = loggingIdentity;

// interface GenericIdentityFn {
//     <Type>(arg: Type[]): Type[]
// }

// let baz: GenericIdentityFn = loggingIdentity;

interface GenericIdentityFn<Type> {
    (arg: Type[]): Type[]
}

// ----------------
// Generic Classes
// ----------------

class GenericNumber<NumType> {
    zeroValue!: NumType;
    add!: (x: NumType, y: NumType) => NumType;
}

const myGenericNumber = new GenericNumber<number>();

myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) { return x + y; };

interface Lengthwise {
    length: number
}

function loggingIdentity2<Type extends Lengthwise>(arg: Type): Type {
    console.log(arg.length);
    return arg;
}


// ----------------
// Using Type Parameters in Generic Constraints
// ----------------

function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
    return obj[key];
}

getProperty({ a: 'a', b: 'b' }, 'a');
getProperty({ a: 'a', b: 'b' }, 'b');
// getProperty({ a: 'a', b: 'b' }, 'c'); // 类型“"c"”的参数不能赋给类型“"a" | "b"”的参数。

// ----------------
// Using Class Types in Generics
// ----------------

function create<Type>(c: { new (): Type }): Type {
    return new c();
}

class BeeKeeper {
    hasMask: boolean = true;
}

class ZooKeeper {
    nametag: string = 'Mikle';
}

class Animal {
    numLegs: number = 4;
}

class Bee extends Animal {
    keeper: BeeKeeper = new BeeKeeper();
}

class Lion extends Animal {
    keeper: ZooKeeper = new ZooKeeper();
}

function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}

createInstance(Lion).keeper.nametag;
createInstance(Bee).keeper.hasMask;

export {};